<?php


namespace Aitels\Design;

use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;

/**
 * 实现表的创建、删除、备份、恢复
 *
 * Class TableManager
 * @package Aitels\Design
 */
class TableManager
{
    protected $store;
    protected $builder;

    public function __construct(Store $store, TableBuilder $builder)
    {
        $this->store = $store;
        $this->builder = $builder;
    }

    public function create($tableName, $tableDefinition)
    {
        return $this->builder->build($tableName, $tableDefinition);
    }

    public function drop($table)
    {
        echo "dropped $table .\n";

        return Schema::dropIfExists($table);
    }


    public function hasChange($table, $definition)
    {
        return $this->store->getHistory($table) !== $definition;
    }

    public function storeDesignHistory($table, $definition)
    {
        $this->store->history($table, $definition);
    }


    public function backup($table)
    {
        if (Schema::hasTable($table)) {
            //将表创建语句及表数据缓存起来以便恢复
            $this->store->backup(
                $table,
                [
                    'definition' => $this->queryTableDefinition($table),
                    'data'       => $this->queryTableData($table),
                ]
            );
        }
    }


    public function recover($table)
    {
        //恢复现场，我们先删除已经创建的新表
        $this->drop($table);
        //备份数据
        $backup = $this->store->getBackup($table);

        //根据备份的表定义创建旧表
        DB::statement($backup['definition']);
        //回填旧表数据, 分批插入
        collect($backup['data'])->chunk(100)->map(function ($chunk) use ($table) {
            DB::table($table)->insert($chunk->all());
        });
        echo "recovered $table .\n";
    }


    public function refill($table, $definition)
    {
        $backup = $this->store->getBackup($table);

        //如果旧数据不为空
        if ($backup['data']) {
            //生成新的表后，我们将旧数据批量插入回新表中，对于删除的字段，其数据应该被过滤掉
            //对于新增的字段，数据库已经有默认值，因此我们不需要指定
            $filtered = $this->filterOldData($backup['data'], $definition);
            //分批插入
            collect($filtered)->chunk(100)->map(function ($chunk) use ($table) {
                DB::table($table)->insert($chunk->all());
            });

            echo "refilled $table .\n";
        }


    }


    public function filterOldData($data, $definition)
    {
        //新字段集合
        $newColumns = collect($definition['columns'])->pluck('name')->all();

        foreach ($data as &$row) {
            $row = Arr::only($row, $newColumns);
        }

        return $data;

    }


    public function queryTableDefinition($table)
    {
        //查询表创建语句
        return DB::select("show create table $table;")[0]->{'Create Table'};
    }

    public function queryTableData($table)
    {
        //返回结果为标准对象集合，须转换为数组以便后面批量插入
        $count = DB::select("select count(1) as total from $table;")[0]->total;

        if ($count > 5000) {
            //我们只保留5000条数据，测试环境下很少有超过这个数量的场景
            return Util::toArrayList(DB::select("select * from $table  limit 5000;"));
        } else {
            return Util::toArrayList(DB::select("select * from $table;"));
        }
    }

}